﻿/*
 * Name:        CollisionManager
 * Description: Handles all collision in the worl
 */
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

using Gyvate.Worldspawn;
using Gyvate.Entities;
using Gyvate.Util;

namespace Gyvate.Collision
{
    internal class CollisionManager
    {
        private Game m_game;

        /*
         * Constructor
         */
        public CollisionManager(Game game)
        {
            m_game = game;
        }

        /*
         * Applies current collisions
         */
        public void ApplyCollision()
        {
            SnakeBody hitBodyPart = CheckBodyCollision();

            if (hitBodyPart != null)
            {
                m_game.SendEvent(EventList.EVENT_GAMEOVER);
                return; //game over, no longer check other collisions
            }

            if (CheckWorldCollision())
            {
                m_game.SendEvent(EventList.EVENT_COLLIDEWORLD);

                if( m_game.GetGameRules().IsWallSolid() )
                    return; //game over, no longer check other collisions
            }

            if (CheckDiagonalCollision())
            {
                m_game.SendEvent(EventList.EVENT_GAMEOVER);
                return; //game over, no longer check other collisions
            }

            BaseEntity hitEntity = CheckEntityCollision();

            if (hitEntity != null)
                hitEntity.Collision();
        }

        /*
         * Checks head collision with other body parts
         */
        private SnakeBody CheckBodyCollision()
        {
            Snake snake = m_game.GetSnake();
            SnakeBody head = snake.GetHead();
            LinkedList<SnakeBody> body = snake.GetBodyList();

            Vector2d headPos = head.GetPos();

            foreach (SnakeBody part in body)
            {
                if (part == head)
                    continue;

                Vector2d partPos = part.GetPos();

                if (headPos != partPos)
                    continue;

                return part;
            }

            return null;
        }

        /*
         * checks collision with playing field
         */
        private bool CheckWorldCollision()
        {
            Vector2d headPos = m_game.GetSnake().GetHead().GetPos();
            Dimensions2d fieldDim = m_game.GetPlayField().GetDimensions();
            int solidWallFix = (m_game.GetGameRules().IsWallSolid() == true) ? 0 : 1;

            if (headPos.x - fieldDim.pos.x <= 0 - solidWallFix)
                return true;
            else if (headPos.y - fieldDim.pos.y <= 0 - solidWallFix)
                return true;
            else if (headPos.x - solidWallFix >= fieldDim.size.x + fieldDim.pos.x)
                return true;
            else if (headPos.y - solidWallFix >= fieldDim.size.y + fieldDim.pos.y)
                return true;

            return false;
        }

        /*
         * Checks collision with snake body by specfied vector origin
         */
        public bool CheckBodyCollisionByPostion(Vector2d ent)
        {
            Snake snake = m_game.GetSnake();
            LinkedList<SnakeBody> body = snake.GetBodyList();

            Vector2d pos = ent;

            foreach (SnakeBody part in body)
            {
                Vector2d partPos = part.GetPos();

                if (pos != partPos)
                    continue;

                return true;
            }

            return false;
        }

        /*
         * Checks head collision with world entities
         */
        private BaseEntity CheckEntityCollision()
        {
            Vector2d headPos = m_game.GetSnake().GetHead().GetPos();
            Dictionary<Vector2d, BaseEntity> pickups = m_game.GetPlayField().GetPickupList();

            BaseEntity ent = null;

            if (pickups.TryGetValue(headPos, out ent))
                return ent;

            return null;
        }

        /*
         * Check collision with world/snake diagonally
         */
        public bool CheckDiagonalCollision()
        {
            Vector2d snakeHeadPos = m_game.GetSnake().GetHead().GetPos();
            Vector2d moveVec = m_game.GetSnake().GetLastMoveVec();

            if (Math.Abs(moveVec.x) + Math.Abs(moveVec.y) != 2)
                return false; //This special case collision is impossible when not moving diagonally

            Dictionary<Vector2d, BaseEntity> pickups = m_game.GetPlayField().GetPickupList();

            Vector2d check1 = new Vector2d(snakeHeadPos);
            check1.x += moveVec.x;

            Vector2d check2 = new Vector2d(snakeHeadPos);
            check2.y += moveVec.y;

            BaseEntity ent = null, ent2 = null;

            if ((pickups.TryGetValue(check1, out ent)) && (pickups.TryGetValue(check2, out ent2)))
                return true;

            Snake snake = m_game.GetSnake();
            LinkedList<SnakeBody> body = snake.GetBodyList();

            bool oneCollision = false;

            foreach (SnakeBody part in body)
            {
                Vector2d partPos = part.GetPos();

                if (oneCollision && (check1 == partPos || check2 == partPos))
                    return true;

                if (check1 == partPos || check2 == partPos)
                    oneCollision = true;
            }
            
            return false;
        }
    }
}
